/*
 * $Id: SFTPCommand.java,v 1.9 2012/01/30 15:52:15 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.common.commands;

import java.io.IOException;
import java.util.List;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.exceptions.FTPCommandException;
import com.ge.healthcare.autosc.common.exceptions.InvalidLoginCredentialsException;
import com.ge.healthcare.autosc.common.exceptions.SFTPCommandException;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.sshtools.j2ssh.SftpClient;
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
import com.sshtools.j2ssh.authentication.KBIAuthenticationClient;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.configuration.ConfigurationException;
import com.sshtools.j2ssh.configuration.ConfigurationLoader;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;

/**
 * Purpose:
 * <ul>
 * <li>Used for SSH to the client dir</li>
 * </ul>
 * 
 * @author Navdeep Mahajan
 */
public class SFTPCommand implements IFTCommand {
	private int iSocketTimeout = 0;

	private SshClient sshClient = null;

	private SftpClient sftpClient = null;

	/**
	 * Initialize the SFTP Loaders
	 * 
	 * @param isdFtFtpSftpDetails
	 * @throws SFTPCommandException
	 */
	public SFTPCommand() throws SFTPCommandException {
		try {
			ConfigurationLoader.initialize(true);
			sshClient = new SshClient();
		} catch (ConfigurationException confExcep) {
			throw new SFTPCommandException(
					"Couldn't initialize the SFTPClient "
							+ confExcep.getMessage());
		}
		iSocketTimeout = Integer.parseInt(ApplicationInfo
				.getInstance()
				.getProperties()
				.getProperty(PropertyConstants.SECURE_TRANSFER_SOCKET_TIMEOUT,
						"60000"));
	}

	/**
	 * Login to the remote system
	 * 
	 * @throws SFTPCommandException
	 * @throws InvalidLoginCredentialsException
	 */
	public void login(String host, String login, String passcode)
			throws SFTPCommandException, InvalidLoginCredentialsException {
		String methodName = "login";
		try {
			sshClient.setSocketTimeout(iSocketTimeout);
			sshClient.connect(host, new IgnoreHostKeyVerification());
		} catch (IOException ioExcep) {
			throw new SFTPCommandException(
					"Error Connecting to the Remote Host !! [Exception] "
							+ ioExcep.getMessage());
		}
		try {
			int result = -1;
			List<?> authType = sshClient.getAvailableAuthMethods(login);
			if (!ASCUtil.isNullOrEmptyList(authType)
					&& authType.contains("password")) {
				ASCLogger.info(this.getClass(), methodName,
						"Connecting using password authentication");
				PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();
				pwd.setUsername(login);
				pwd.setPassword(passcode);
				// Authenticate the user
				result = sshClient.authenticate(pwd);
			} else {
				ASCLogger.info(this.getClass(), methodName,
						"Connecting using KBI authentication");
				KBIAuthenticationClient kbi = new KBIAuthenticationClient();
				kbi.setUsername(login);
				ASCKBIRequestHandler kbiHandler = new ASCKBIRequestHandler();
				kbiHandler.setPassCode(passcode);
				kbi.setKBIRequestHandler(kbiHandler);
				// Authenticate the user
				result = sshClient.authenticate(kbi);
			}

			if (result != AuthenticationProtocolState.COMPLETE) {
				sshClient.disconnect();
				throw new InvalidLoginCredentialsException(
						"Invalid Login Name / Password for Connecting to the Remote Host !!");
			}
			// Opens the sftp client
			sftpClient = sshClient.openSftpClient();
			if (sftpClient == null) {
				throw new SFTPCommandException(
						"Error Initializing the FTP/SSH Client ");
			}
		} catch (IOException ioExcep) {
			throw new SFTPCommandException(
					"Invalid Login Name / Password for Connecting to the Remote Host !! [Exception] "
							+ ioExcep.getMessage());
		}
	}

	/**
	 * Logout from the remote system
	 * 
	 * @throws SFTPCommandException
	 */
	public void logout() throws SFTPCommandException {
		try {
			if (!ASCUtil.isNullOrEmptyObject(sftpClient)) {
				sftpClient.quit();
			}
		} catch (IOException ioExcep) {
			throw new SFTPCommandException(
					"Error Returning the FTPBean to the Pool [Exception] "
							+ ioExcep.getMessage());
		} finally {
			sftpClient = null;
			if (!ASCUtil.isNullOrEmptyObject(sshClient)) {
				sshClient.disconnect();
			}
			sshClient = null;
		}
	}

	/**
	 * Transfer file to the remote machine
	 * 
	 * @param localFile
	 * @param remoteFile
	 * @throws SFTPCommandException
	 */
	public void putFile(String localFile, String remoteFile)
			throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(localFile)) {
			throw new SFTPCommandException(
					new StringBuffer()
							.append("localFile : ")
							.append(localFile)
							.append(" of the putFile method of SFTPCommand can't be Null.")
							.toString());
		} else if (ASCUtil.isNullOrEmpty(remoteFile)) {
			throw new SFTPCommandException(
					new StringBuffer()
							.append("remoteFile : ")
							.append(remoteFile)
							.append(" of the putFile method of SFTPCommand can't be Null.")
							.toString());
		}
		try {
			sftpClient.put(localFile, remoteFile);
		} catch (IOException ioExcep) {
			throw new SFTPCommandException(new StringBuffer()
					.append("Error copying the File ").append(localFile)
					.append(" to a Remote System as").append(remoteFile)
					.append(" [ Exception] :").append(ioExcep.getMessage())
					.toString());
		}
	}

	public void getFile(String remoteFile, String localFile)
			throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(localFile)) {
			throw new SFTPCommandException(
					new StringBuffer()
							.append("localFile : ")
							.append(localFile)
							.append(" of the getFile method of SFTPCommand can't be Null.")
							.toString());
		} else if (ASCUtil.isNullOrEmpty(remoteFile)) {
			throw new SFTPCommandException(
					new StringBuffer()
							.append("remoteFile : ")
							.append(remoteFile)
							.append(" of the getFile method of SFTPCommand can't be Null.")
							.toString());
		}
		try {
			sftpClient.get(localFile, remoteFile);
		} catch (IOException ioExcep) {
			throw new SFTPCommandException(new StringBuffer()
					.append("Error transferring the File ").append(localFile)
					.append(" from a Remote System as").append(remoteFile)
					.append(" [ Exception] :").append(ioExcep.getMessage())
					.toString());
		}
	}

	/**
	 * Method that provides the facility of making the directory on a remote
	 * location.
	 * 
	 * @param pathname
	 * @throws SFTPCommandException
	 */
	public void mkdir(String pathname) throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(pathname)) {
			throw new SFTPCommandException(
					new StringBuffer()
							.append("pathname : ")
							.append(pathname)
							.append(" of the mkdir method of SFTPCommand can't be Null.")
							.toString());
		}
		try {
			sftpClient.mkdir(pathname);
		} catch (IOException ae) {
			throw new SFTPCommandException(new StringBuffer()
					.append("Error creating Directory ").append(pathname)
					.append(" Permission denied. Exception : ")
					.append(ae.getMessage()).toString());
		}
	}

	/**
	 * @see com.ge.healthcare.autosc.common.commands.IFTCommand#chmod(java.lang.String,
	 *      java.lang.String, java.lang.String, java.lang.String)
	 */
	public void chmod(String ownerPermission, String staffPermission,
			String othersPermission, String path) throws SFTPCommandException {
		String chmodCommand = new StringBuffer("0").append(ownerPermission)
				.append(staffPermission).append(othersPermission).toString();
		try {
			if ("0777".equals(chmodCommand)) {
				sftpClient.chmod(0777, path);
			} else if ("0755".equals(chmodCommand)) {
				sftpClient.chmod(0755, path);
			} else if ("0754".equals(chmodCommand)) {
				sftpClient.chmod(0754, path);
			} else if ("0744".equals(chmodCommand)) {
				sftpClient.chmod(0744, path);
			} else if ("0555".equals(chmodCommand)) {
				sftpClient.chmod(0555, path);
			} else if ("0544".equals(chmodCommand)) {
				sftpClient.chmod(0544, path);
			} else if ("0444".equals(chmodCommand)) {
				sftpClient.chmod(0444, path);
			} else {
				throw new SFTPCommandException(
						"Configuration not available for specified permissions");
			}
		} catch (NumberFormatException numFormatException) {
			throw new SFTPCommandException(new StringBuffer()
					.append("Permission format not correct ")
					.append(chmodCommand)
					.append(numFormatException.getMessage()).toString(),
					numFormatException);
		} catch (IOException ioException) {
			throw new SFTPCommandException(new StringBuffer()
					.append("Error changing mode to ").append(chmodCommand)
					.append(ioException.getMessage()).toString(), ioException);
		}
	}

	/**
	 * Renames the file
	 * 
	 * @param oldName
	 *            - The existing name of the file
	 * @param newName
	 *            - New name of the file
	 * @throws SFTPCommandException
	 *             - indicates renaming failed
	 */
	public void mv(String oldName, String newName) throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(oldName)) {
			throw new SFTPCommandException(
					"OldName has not been specified in the rename method of FTPCommand");
		}
		if (ASCUtil.isNullOrEmpty(newName)) {
			throw new SFTPCommandException(
					"NewName has not been specified in the rename method of FTPCommand");
		}
		try {
			sftpClient.rename(oldName, newName);
		} catch (IOException ioException) {
			try {
				sftpClient.rm(newName, true, false);
				sftpClient.rename(oldName, newName);
			} catch (IOException innerIOException) {
				throw new SFTPCommandException(new StringBuffer()
						.append("Couldn't rename from ").append(oldName)
						.append(" to ").append(newName).toString(),
						innerIOException);
			}
		}
	}

	/**
	 * Method that provides the facility of making directories on a remote
	 * location.
	 * 
	 * @param pathname
	 * @throws SFTPCommandException
	 */
	public void mkdirs(String pathname) throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(pathname)) {
			throw new SFTPCommandException(
					new StringBuffer()
							.append("pathname : ")
							.append(pathname)
							.append(" of the mkdirs method of SFTPCommand can't be Null.")
							.toString());
		}
		sftpClient.mkdirs(pathname);
	}

	/**
	 * Method that shows the current working directory at the remote location.
	 * 
	 * @return
	 * @throws SFTPCommandException
	 */
	public String pwd() {
		return sftpClient.pwd();
	}

	/**
	 * Method that provides the facility of changing the current working
	 * directory at the remote location.
	 * 
	 * @param pathname
	 * @return
	 * @throws SFTPCommandException
	 */
	public boolean cd(String pathname) throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(pathname)) {
			throw new SFTPCommandException(new StringBuffer()
					.append("pathname : ").append(pathname)
					.append(" of the cd method of SFTPCommand can't be Null.")
					.toString());
		}
		try {
			sftpClient.cd(pathname);
			return true;
		} catch (IOException ae) {
			return false;
		}
	}

	/**
	 * @see com.ge.healthcare.service.mdex.commands.IFTCommand#cd(java.lang.String,
	 *      boolean)
	 */
	public boolean cd(String dir, boolean throwExcep)
			throws SFTPCommandException {
		if (!this.cd(dir)) {
			if (throwExcep) {
				throw new SFTPCommandException(new StringBuffer()
						.append("The Required dir [").append(dir)
						.append("] is missing").toString());
			}
		}
		return true;
	}

	/**
	 * Method that provides the facility of changing the current working
	 * directory at the remote location.
	 * 
	 * @param pathname
	 * @return
	 * @throws SFTPCommandException
	 */
	public boolean lcd(String pathname) throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(pathname)) {
			throw new SFTPCommandException(new StringBuffer()
					.append("pathname : ").append(pathname)
					.append(" of the lcd method of SFTPCommand can't be Null.")
					.toString());
		}
		try {
			sftpClient.lcd(pathname);
			return true;
		} catch (IOException ae) {
			throw new SFTPCommandException(ae);
		}
	}

	/**
	 * Method to list contents of a directory
	 * 
	 * @param pathname
	 *            - The directory path which needs to be listed
	 * @return - List containing file information which were found in the
	 *         directory
	 * @throws SFTPCommandException
	 *             - Exception thrown in case we cannot list the contents
	 */
	public List<?> ls(String pathname) throws SFTPCommandException {
		if (ASCUtil.isNullOrEmpty(pathname)) {
			throw new SFTPCommandException(new StringBuffer()
					.append("pathname : ").append(pathname)
					.append(" of the ls method of SFTPCommand can't be Null.")
					.toString());
		}
		try {
			List<?> lsList = sftpClient.ls(pathname);
			return lsList;
		} catch (IOException ae) {
			throw new SFTPCommandException(ae);
		}
	}

	/**
	 * Never invoke me i am here for interface implementation perse
	 * 
	 * @see com.ge.healthcare.autosc.common.commands.IFTCommand#setTransferMode(int)
	 */
	public void setTransferMode(int mode) throws FTPCommandException {
		ASCLogger
				.error(this.getClass(),
						"setTransferMode",
						"Why are you invoking me? There is no implementation in SFTP Client to transfer in prespecified mode, default sftp modes are selected");
	}

	/**
	 * Never invoke me i am here for interface implementation perse
	 * 
	 * @see com.ge.healthcare.autosc.common.commands.IFTCommand#setBinaryTransferMode()
	 */
	public void setBinaryTransferMode() throws FTPCommandException {
		this.setTransferMode(0);
	}
}
